﻿using System;
using System.Collections;
using System.Text;

namespace FinalProjectLogicLayer
{
    /// <summary>
    /// ystem
    /// </summary>
    public class FileInfo
    {
        private String filePath;
        /// <summary>
        /// This property sets or gets the filePath
        /// </summary>
        public String FilePath
        {
            get
            {
                return filePath;
            }
            set
            {
                filePath = value;
            }
        }

        private String commnets;
        /// <summary>
        /// This file sets or gets the comments of the file
        /// </summary>
        public String Comments
        {
            get
            {
                return commnets;
            }
            set
            {
                commnets = value;
            }
        }
        private int divisionParameter;
        /// <summary>
        /// This property sets or gets to how many parts the file will be devided
        /// </summary>
        public int DivisionParameter
        {
            get
            {
                return divisionParameter;
            }
            set
            {
                if (value > 0 )
                    if (value!=divisionParameter)
                        divisionParameter = value;
                else
                    divisionParameter = 1;
            }
        }

        /// <summary>
        /// label that describes this file
        /// </summary>
        private String label;
        /// <summary>
        /// This property sets or gets the label describing the file
        /// </summary>
        public String Label
        {
            get
            {
                return label;

            }
            set
            {
                label = value;
            }
        }

        /// <summary>
        /// This method returns the contents of the file as a string
        /// </summary>
        /// <returns>String containing the contents of file</returns>
        public String fileAsString()
        {
            return FeatureExtraction.fileToString(this.FilePath);
        }
        /// <summary>
        /// Constructor
        /// </summary>
        public FileInfo()
        {
            this.filePath = "";
            this.label = "";
            this.divisionParameter = 1;
            this.commnets = "";
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public FileInfo(String filePath)
        {
            this.filePath = filePath;
            this.label = "";
            this.divisionParameter = 1;
            this.commnets = "";
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="filepath">path of the file</param>
        /// <param name="label">label for the file file are grouped together by their labels</param>
        /// <param name="divisionParameter">Defines to how many parts this file will be devided</param>
        /// <param name="comments">Comments about this file</param>
        public FileInfo(String filepath, String label, int divisionParameter, String comments)
        {
            this.filePath = filepath;
            this.label = label;
            this.divisionParameter = divisionParameter;
            this.commnets = comments;
        }
        /// <summary>
        /// this method returns a string representing the feature vectors
        /// </summary>
        /// <returns>a string representing the feature vectors values</returns>
        private String FeatureVectorsString()
        {
            String featureVectorString = "";
            if (featureVectors!=null)
                foreach (double[] curFeatureVector in featureVectors)
                {
                    featureVectorString += "[";
                    foreach (double val in curFeatureVector)
                        featureVectorString += val + ",";
                    featureVectorString += "]\n";
                }
            return featureVectorString;
        }
        public override String ToString()
        {
            String val = "File path: " + this.FilePath + "\n" +
                         "File label: " + this.Label + "\n" +
                         "Devision Parameter: " + this.DivisionParameter + "\n" +
                         "Feature Vectors:\n\t" + FeatureVectorsString() +
                         "Comments: " + this.Comments + "\n\n";
            return val;
        }
        /// <summary>
        /// This array list will hold the feature vectors of all the file parts of this file
        /// </summary>
        /// <remarks>CSV</remarks>
        private ArrayList featureVectors = new ArrayList();
        /// <summary>
        /// Property returns an ArrayList of double arrays containing the 
        /// </summary>
        public ArrayList FeatureVectors
        {
            get
            {
                return featureVectors;
            }
            set
            {
                Console.WriteLine("set method called in FeatureVectores Property in a FileInfoAndVectorInfo");
            }
        }

        /// <summary>
        /// the serial number of the dictionary used to create the feature vectors
        /// </summary>
        private String dictionaryID;

        public String DictionaryID
        {
            get { return dictionaryID; }
            set { dictionaryID = value; }
        }
    }
/******************************************************************************************************/
/***********************FILE LIST*********************************************************************/
/******************************************************************************************************/
    /// <summary>
    /// This class is an extension of Array list specialized for FileInfo instances
    /// </summary>
    public class FileList : System.Collections.ArrayList
    {
       public FileList()
        {

        }
        /// <summary>
        /// This method removes a fileInfo instance from the list by their filename
        /// </summary>
        /// <param name="filePath"></param>
       public void Remove(String filePath)
       {
           this.Remove(get(filePath));
       }

        public override int Add(object newFile)
        {
            return base.Add(newFile);
        }

        /// <summary>
        /// This method gets a specified file from the list
        /// </summary>
        /// <param name="filepath">the file path of the file to get</param>
        /// <returns>returns the file if found or null otherwise</returns>
        public FileInfo get(String filepath)
        {
            foreach (FileInfo file in this) //go over all the files srotred in arraylist
            {
                if (file.FilePath.Equals(filepath))
                {
                    return file;
                }
            }
            return null;
        }

        public override String ToString()
        {
            String outString = "";
            foreach (FileInfo file in this) //go over all the files srotred in arraylist
            {
                outString += file.ToString();
            }
            return outString;
        }
        /// <summary>
        /// Indexer using file names
        /// </summary>
        /// <param name="index">String with the file name</param>
        /// <returns>File info of the selected index</returns>
     
        public FileInfo this[String index]
        {
            get
            {
                FileInfo temp = get(index);
                if (temp == null)
                    throw new IndexOutOfRangeException();
                return temp;

            }
            set //Add FileInfo instance at a selected index instade of the old one 
            {
                FileInfo temp = get(index);
                if (temp==null)
                    throw new IndexOutOfRangeException();
                this[this.IndexOf(temp)] = value;
            }
        }
        private ArrayList filesAsDataPoints = null;
        /// <summary>
        /// This method converts the file to an Arraylist containing local data points
        /// This will be the input for the clustering procedure
        /// </summary>
        /// <returns>Array list of data points</returns>
        private ArrayList convertFilesAsDataPoints()
        {
            if (filesAsDataPoints==null)
                filesAsDataPoints = new ArrayList();
            filesAsDataPoints.Clear();//empty the previos list
            foreach (FileInfo fileInfo in this) //populate the list with data
            {
                for (int i=0; i<fileInfo.DivisionParameter; i++)
                {
                    DataPoint currentPoint = new DataPoint((double[])fileInfo.FeatureVectors[i]); //create a new dataPoint with the feature vectore 
                    currentPoint.Label = fileInfo.Label;
                    currentPoint.SourceFile = fileInfo.FilePath + " PART " + (i+1);
                    currentPoint.Comment = fileInfo.Comments;
                    currentPoint.Part = i + 1;
                    filesAsDataPoints.Add(currentPoint);
                }
            }
            return filesAsDataPoints;
        }
        /// <summary>
        /// This method returns the coordinates of the entire File list as an ArrayList containing double[]
        /// </summary>
        /// <returns></returns>
        public ArrayList getCsvVectores()
        {
            ArrayList csvVectors = new  ArrayList();
            csvVectors.Clear();
            convertFilesAsDataPoints();
            foreach (DataPoint currPoint in filesAsDataPoints)
            {
                csvVectors.Add(currPoint.PointCoordinates);
            }
            return csvVectors;
        }
        /// <summary>
        /// This methods get's the clustering results which is an array list conatining arrays of integers each representing a cluster
        /// and retuns an array list which containg an arrayList wich holds arrays of dataPoint each array represents a cluster
        /// </summary>
        /// <param name="clusteringResults">Array Lists containing int[]'s each represents indexes of csv's belonging cluster</param>
        /// <returns>Array list containing DataPoints</returns>
        public ArrayList getClusters(ArrayList clusteringResults)
        {
            if (filesAsDataPoints == null) return null;
            ArrayList output = new ArrayList();
            int clusterNumber = 0; //Cluster number to assign each data point
            
            //remove empty clusters
            while (clusteringResults.Count!=0)
            {
                if (((int[])clusteringResults[clusteringResults.Count - 1]).Length == 0)
                {
                    clusteringResults.RemoveAt(clusteringResults.Count - 1);
                }
                else
                    break;
                        
            }

            int[] medoids = new int[clusteringResults.Count];
            int i = 0;
            foreach (int[] clusterindexs in clusteringResults)
            {
                medoids[i++] = clusterindexs[0];
            }
            foreach (int[] clusterindexs in clusteringResults)
            {
                if (clusterindexs.Length==0) continue; //if the cluster is empty
                clusterNumber++;
                DataPoint[] cluster = new DataPoint[clusterindexs.Length];
                i = 0;
                foreach (int index in clusterindexs)
                {
                    cluster[i++] = (DataPoint)filesAsDataPoints[index];
                    cluster[i - 1].BelongsToCluster = clusterNumber;
                    //Distance from point to medoid
                    cluster[i - 1].Distance = Clustering.PamClustering.LastDisMatCalculated[clusterindexs[0], index];
                    //Distance from all the medoids to this point
                    double[] distanceFromAllMedoids = new double[clusteringResults.Count];
                    for (int j=0; j<clusteringResults.Count; j ++)
                    {
                        distanceFromAllMedoids[j] = Clustering.PamClustering.LastDisMatCalculated[medoids[j], index];
                    }
                    cluster[i - 1].DistanceFromMedoids = distanceFromAllMedoids;
                }
                output.Add(cluster);
            }
            return output;
        }
        public String[] getFileNames()
        {
            String[] fileNames = new String[this.Count];
            int i = 0;
            foreach (FileInfo fileInfo in this) //populate the list with data
            {

                    fileNames[i++] = fileInfo.FilePath;
            }
            return fileNames;
        }
        private int lastStringMatchingDistance = 0;

        public int StringMachingDistanceUsed
        {
            get { return lastStringMatchingDistance; }
            set {
                if (lastStringMatchingDistance != value)
                    ValidCSVs = false;
                lastStringMatchingDistance = value; 
                }
        }

        private int lastSizeOfTheAlphaBet = 0;

        public int LastSizeOfTheAlphaBet
        {
            get { return lastSizeOfTheAlphaBet; }
            set {
                if (lastSizeOfTheAlphaBet != value)
                    ValidCSVs = false;
                lastSizeOfTheAlphaBet = value; 
                }
        }
        private bool validCSVs = false;

        public bool ValidCSVs
        {
            get { return validCSVs; }
            set { validCSVs = value; }
        }


    }

/******************************************************************************************************/
/***********************LOCAL DATA POINTS**************************************************************/
/******************************************************************************************************/
    /// <summary>
    /// This class specifies how this implemetations data point look like
    /// each point contains it's feature vector and details about origin file comments and label
    /// </summary>
    class DataPoint
    {
        /// <summary>
        /// Empty constructor
        /// </summary>
        public DataPoint()
        {
        }
        /// <summary>
        /// Constructor 
        /// </summary>
        /// <param name="coordinates">the coordinates of the point</param>
        public DataPoint(double[] coordinates)
        {
          PointCoordinates = coordinates;
        }
        
        private double[] pointCoordinates;
        /// <summary>
        /// Set's or Gets PointCoordinates
        /// </summary>
        public double[] PointCoordinates
        {
            get { return pointCoordinates; }
            set { pointCoordinates = value; }
        }

        private String comment;
        /// <summary>
        /// Set's or gets point comments
        /// </summary>
        public String Comment
        {
            get { return comment; }
            set { comment = value; }
        }
        private String sourceFile;
        /// <summary>
        /// Set's or gets points source file name
        /// </summary>
        public String SourceFile
        {
            get { return sourceFile; }
            set { sourceFile = value; }
        }
        private String label;
        /// <summary>
        /// set's or gets source Label
        /// </summary>
        public String Label
        {
            get { return label; }
            set { label = value; }
        }
        private int part;
        /// <summary>
        /// set's or get's source part of the original file
        /// </summary>
        public int Part
        {
            get { return part; }
            set { part = value; }
        }
        private int belongsToCluster = -1;
        /// <summary>
        /// This property set's or gets this point cluster number (-1 this point is not assigned to a cluster)
        /// </summary>
        public int BelongsToCluster
        {
            get { return belongsToCluster; }
            set { belongsToCluster = value; }
        }

        private double distance = 0;
        /// <summary>
        /// sets or get the distance of point from it's medoid
        /// </summary>
        public double Distance
        {
            get { return distance; }
            set { distance = value; }
        }
        
        private double[] distanceFromMedoids = null;
        /// <summary>
        /// sets or get the distance of point from it's medoid
        /// </summary>
        public double[] DistanceFromMedoids
        {
            get { return distanceFromMedoids; }
            set { distanceFromMedoids = value; }
        } 
        /// <summary>
        /// Retuns the data point as String
        /// </summary>
        /// <returns>datapoints information</returns>
        override public String  ToString()
        {
            String dataPointString = "";
            dataPointString += "*********************************\n";
            dataPointString += "Name: " + this.SourceFile+"\n";
            String csvString = ""; //convert each csv to String

                foreach (double num in this.PointCoordinates)
                    csvString += "["+num + "],";
                if (csvString.Length>0)
                    csvString = csvString.Substring(0, csvString.LastIndexOf(",")); //delete last ","
            
            
            dataPointString += "Coordinates: " + csvString + "\n";
            dataPointString += "Distance from own Medoid: " + this.Distance + "\n";
            dataPointString += "Distance from other Medoid: \n";
            for (int i = 0; i < this.DistanceFromMedoids.Length; i++)
            {
                if ((i + 1) == this.belongsToCluster) continue;
                dataPointString += "\tMedoid of cluster " + (i + 1) + ": " + this.DistanceFromMedoids[i] + "\n";
            }
            
            dataPointString += "Comments: " + this.Comment + "\n";
            dataPointString += "Label: " + this.Label + "\n";
            dataPointString += "Part: " + this.part + "\n";
            dataPointString += "Member of Cluster number: " + this.belongsToCluster + "\n";
            dataPointString += "*********************************\n";
            return dataPointString;
        }
        public String ToHtml()
        {
            String dataPointString = "";

            dataPointString += "<HR><TABLE class=\"CLUSTER\">";
            dataPointString += HtmlReport.addTableRowHeader("File name:", this.sourceFile);
            String csvString = ""; //convert each csv to String
            foreach (double num in this.PointCoordinates)
                csvString += "[" + num + "],";
            if (csvString.Length > 0)
                csvString = csvString.Substring(0, csvString.LastIndexOf(",")); //delete last ","

            


            dataPointString += HtmlReport.addTableRow("Coordinates:",csvString);
            dataPointString += HtmlReport.addTableRow("Distance from Medoids");
            dataPointString += HtmlReport.addTableRow("","OWN MEDOID",""+this.distance);
            
            
            for (int i = 0; i < this.DistanceFromMedoids.Length; i++)
            {
                if ((i + 1) == this.belongsToCluster) continue;
                dataPointString += HtmlReport.addTableRow("", "Medoid of cluster " + (i + 1), "" + this.DistanceFromMedoids[i]);
            }
            dataPointString += HtmlReport.addTableRow("Comment:", "" + this.comment);
            dataPointString += HtmlReport.addTableRow("Label:", "" + this.Label);
            dataPointString += HtmlReport.addTableRow("This is fraction number:", "" + this.part);
            dataPointString += HtmlReport.addTableRow("Member of cluster", "" + this.belongsToCluster);
            dataPointString += "</TABLE><BR>";


            return dataPointString;
        }

        
    }
}

